import { shuffle, cloneDeep, throttle, debounce } from 'lodash-es'
import { reactive, computed, watch, onBeforeMount, onMounted, onBeforeUnmount, inject } from 'vue'
import { isFunc, isObject, msg, eventCenter, log, setInitData } from '../utils'

export default (props: any) => {

    const state = reactive({
        searchData: {} as any,
        initData: {} as any, // 初始值, 全局调用
        dialog: {
            visible: false,
            config: {} as any,
            body: [] as any[],
        },
        tableHeight: undefined as any,
        tableData: {
            data: [],
            loading: false,
        },
        pageData: {
            pageNum: 1,
            pageSize: 15,
            totalRows: 0
        }
    })

    const defaultSearchButtons: any = [
        {
            ui: 'button',
            label: '查询',
            type: 'primary',
            icon: 'Search',
            actionName: 'search'
        },
        {
            ui: 'button',
            label: '重置',
            actionName: 'searchReset'
        },
    ]

    // 默认查询表单（从表格列中取）
    const defaultSearchForm = computed(() => {
        if (!props.options.body) return []
        return props.options.body.column
            ? props.options.body.column
                .filter((item: any) => !['button'].includes(item.ui) && item.search)
                .map((item: any) => {
                    const conf = {
                        label: item.label,
                        key: item.key,
                        clearable: true,
                        ...item.common ? item.common : {},
                        ...item.search ? item.search : {},
                    }
                    if (conf.ui === 'input-number') {
                        conf.controls = false
                    }
                    
                    return conf
                })
            : []
    })

    const defaultForm = computed(() => {
        if (!props.options.body) return []
        return props.options.body.column
            ? props.options.body.column
                .filter((item: any) => !['button'].includes(item.ui) && item.form)
                .map((item: any) => {
                    return {
                        label: item.label,
                        key: item.key,
                        ...item.common ? item.common : {},
                        ...item.form ? item.form : {},
                    }
                })
            : []
    })

    // 生成表单配置数据
    const createToolBarFormItems = (items = [], isAppendSearchForm: boolean, searchForm = [], defaultSearchButtons = []) => {
        const formItems: any[] = isAppendSearchForm ? [ ...items, ...searchForm, ...defaultSearchButtons ] : [ ...items ]
        // 排序处理
        return formItems.some(item => item.sort)
            ? formItems.sort((a, b) => a.sort - b.sort)
            : formItems
    }
    const toolBar = computed(() => {
        if (!props.options.body || !props.options.body.toolBar) return []
        return props.options.body.toolBar
            ? props.options.body.toolBar.map((item: any) => ({
                searchPosition: item.searchPosition,
                left: createToolBarFormItems(item.left, item.searchPosition === 'left', defaultSearchForm.value, defaultSearchButtons),
                right: createToolBarFormItems(item.right, item.searchPosition === 'right', defaultSearchForm.value, defaultSearchButtons)
            }))
            : []
    })

    const tableColumn = computed(() => {
        if (!props.options.body) return []
        return props.options.body.column
            ? props.options.body.column
                .map((item: any) => {
                    const { form, search, common, ..._item} = item
                    return {
                        ..._item,
                        ...item.common ? item.common : {},
                    }
                })
            : []
    })

    // 事件处理
    const handleEvent = (params: any) => {
        eventCenter(params, action)
    }

    // 按钮事件
    const action = {
        // 表单弹窗
        dialogForm: (params: any) => {
            createDialog({ params })
        },
        // 空白表单弹窗
        emptyDialogForm: (params: any) => {
            createDialog({ params, isEmpty: true })
        },
        // 查询
        search: ({item, form}: any) => {
            state.searchData = form
            getTableData();
        },
        // 查询重置
        searchReset: ({item, form}: any) => {
            state.searchData = {}
            if (props.options.config && props.options.config.defaultSearchData) {
                setSearchData(props.options.config.defaultSearchData)
            }
            state.pageData.pageNum = 1
            getTableData();
        },
        // 刷新
        refresh: () => {
            getTableData();
        },
        // 发起请求
        fetch: async(params: any) => {
            const {item, form} = params
            if (!item.fetch) return log(`缺少 fetch 配置`, '', 'warn')
            if (!item.fetch.api) return log(`缺少 fetch.api 配置`, '', 'warn')
            if (item.fetch.validate && !item.fetch.validate({ form, item, msg })) return
            let p = {}
            item.loading = true
            if (item.fetch.before) p = item.fetch.before({ form, item })
            try {
                const { data } = await item.fetch.api(p)
                if (item.fetch.success && isFunc(item.fetch.success)) item.fetch.success({ data, msg, item })
                if (item.fetch.complete && isFunc(item.fetch.complete)) item.fetch.complete({ msg })
                item.loading = false
                getTableData();
            } catch (error) {
                item.loading = false
                if (item.fetch.fail && isFunc(item.fetch.fail)) item.fetch.fail(error)
            }
        },
        // 确认框
        confirm: (params: any) => {
            const {item, form} = params
            if (!item.confirm) return log(`缺少 confirm 配置`, '', 'warn')
            ElMessageBox.confirm(
                `${item.confirm.content}${item.confirm.tipKey ? ` ${form[item.confirm.tipKey]}` : ''}?`,
                item.confirm.title,
                {
                    type: 'warning',
                    callback: async (action: any) => {
                        if (action === 'confirm') {
                            if (!item.fetch) return log(`缺少 fetch 配置`, '', 'warn')
                            if (item.fetch.validate && !item.fetch.validate({ form, item, msg })) return
                            let p = {}
                            state.tableData.loading = true
                            if (item.fetch.before) p = item.fetch.before({ form, item })
                            if (!item.fetch.api) return log(`缺少 fetch.api 配置`, '', 'warn')
                            try {
                                const { data } = await item.fetch.api(p)
                                if (item.fetch.success && isFunc(item.fetch.success)) item.fetch.success({data, msg, item})
                                if (item.fetch.complete && isFunc(item.fetch.complete)) item.fetch.complete({msg})
                                state.tableData.loading = false
                                getTableData();
                            } catch (error) {
                                state.tableData.loading = false
                                if (item.fetch.fail && isFunc(item.fetch.fail)) item.fetch.fail(error)
                            }
                        }
                    }
                }
            ).catch(() => { });
        },
    }

    type Action = typeof action
    type TableActionKey = keyof Action

    const getParams = () => {
        const _p = { ...state.searchData }
        if (props.options.config.paging) {
            _p.pageNum = state.pageData.pageNum
            _p.pageSize = state.pageData.pageSize
        }
        return props.options.config.fetch.before
            ? props.options.config.fetch.before({ ..._p })
            : _p;
    }
    
    const getTableData = async () => {
        if (!(props.options.config && props.options.config.fetch)) return log('缺少', 'options.config.fetch 配置', 'warn') 
        if (!props.options.config.fetch.api) return log(`缺少`, 'options.config.fetch.api 配置', 'warn')
        if (props.options.config.fetch.validate && !props.options.config.fetch.validate({ params: {...state.searchData}, msg })) return
        state.tableData.loading = true;
        const params = getParams()
        try {
            if (props.options.config.log) log(`参数`, params)
            const { data } = await props.options.config.fetch.api(params);
            if (!data) return
            if (props.options.config.paging) {
                let _totalKey = 'total'
                let _dataKey = 'list'
                if (isObject(props.options.config.paging)) {
                    const { totalKey, dataKey } = props.options.config.paging
                    if (totalKey) _totalKey = totalKey
                    if (dataKey) _dataKey = dataKey
                }
                state.tableData.data = props.options.config.fetch.success && isFunc(props.options.config.fetch.success)
                    ? props.options.config.fetch.success({ data: data[_dataKey]})
                    : data[_dataKey]
                state.pageData.totalRows = data[_totalKey]
            } else {
                state.tableData.data = props.options.config.fetch.success && isFunc(props.options.config.fetch.success)
                    ? props.options.config.fetch.success({ data })
                    : data
            }
            props.options.config.fetch.complete && props.options.config.fetch.complete(data)
            state.tableData.loading = false;
        } catch (error) {
            state.tableData.loading = false;
            props.options.config.fetch.fail && props.options.config.fetch.fail(error)
        }
    };
    
    // 分页改变
    const handleChange = ({pageSize, currentPage}: any) => {
        if (pageSize) state.pageData.pageSize = pageSize
        state.pageData.pageNum = currentPage
        getTableData();
    };

    const dialogFormClosed = (data: any) => {
        state.dialog.visible = false
        if (!data) return // 这里判断data, 表示只能【在调用接口后】才能往下执行
        if ([undefined, true].includes(state.dialog.config.queryTableData)) {
            if ([true].includes(state.dialog.config.toFirstPage)) {
                state.pageData.pageNum = 1
            }
            getTableData()
        }
    }

    // 创建弹窗
    const createDialog = ({params, isEmpty = false}: any) => {
        const { item, form } = params
        if (!item.options) return log(item.label, '缺少 options 配置', 'warn')
        state.dialog.config = item.options.config ? cloneDeep(item.options.config) : {}
        state.dialog.config.title = item.label
        state.dialog.config.parentData = cloneDeep({ ...form })
        state.dialog.config.parentInitData = cloneDeep({ ...state.initData })
        state.dialog.body = cloneDeep([
            ...isEmpty ? [] : defaultForm.value,
            ...item.options.body || []
        ])
        state.dialog.visible = true
    }

    const setSearchData = (obj: any) => {
        for (const key in obj) {
            state.searchData[key] = isFunc(obj[key]) ? obj[key]({ initData: state.initData }) : obj[key]
            if (props.options.config.log) log(`执行 searchData ${key}`, '')
        }
    }
    // 计算表格高度
    const layoutConfig = inject('layoutConfig')
    const getTableHeight = () => {
        const config: any = layoutConfig
        const headerH = config
            ? config.headerBar.height + (config.tabBar.show ? config.tabBar.height : 0)
            : 0
        const pagingH = props.options.config.paging ? 44 : 0
        const toolBarH = (toolBar.value && toolBar.value.length)
            ?  (52 + 5) * toolBar.value.length
            : 0
        const gutterH = 15 * 2
        return window.innerHeight - headerH - toolBarH - gutterH - pagingH
    }

    state.tableHeight = [true, undefined].includes(props.options.config.heightAuto) ? getTableHeight() : undefined

    const init = async() => {
        if (props.options.config.initData) {
            await setInitData(props.options.config.initData, state.initData, {}, props.options.config.serialization, props.options.config.log)
        }
        if (props.options.config && props.options.config.defaultSearchData) {
            setSearchData(props.options.config.defaultSearchData)
        }
        if (props.options.config && props.options.config.paging && props.options.config.paging.pageSize) {
            state.pageData.pageSize = props.options.config.paging.pageSize
        }
        if ([undefined, true].includes(props.options.config.autoInit)) getTableData();
    }

    const getData = () => {
        return state.tableData.data
    }
    
    // 异步加载数据
    watch(
        () => props.options.config.autoInit,
        (val) => {
            if (val) init()
        },
        { deep: true }
    )
    
    onBeforeMount(() => {
        init()
    });
    
    onMounted(() => {
        if ([true, undefined].includes(props.options.config.heightAuto)) {
            window.onresize = function() {
                state.tableHeight = getTableHeight();
            }
        }
    })
    
    onBeforeUnmount(() => {
        if ([true, undefined].includes(props.options.config.heightAuto)) {
            window.onresize = null
        }
    })

    return {
        state,
        toolBar,
        tableColumn,
        createDialog,
        setSearchData,
        handleEvent,
        getTableData,
        getData,
        refresh: action.refresh,
        handleChange,
        dialogFormClosed
    }
}